package com.xxxx.server.config.security.compoent;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// Jwt登录授权过滤器
public class JwtAuthencationTokenFilter extends OncePerRequestFilter {

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private UserDetailsService userDetailsService;

    // 前置拦截
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        // 使用request获取前端传来的
        // 需要用@value获取，在配置中是有的
        // 获取要验证的头 authHeader
        String authHeader = request.getHeader(tokenHeader);
        // 验证：如果不存在或者说以 tokenHead 存在
        if(null!=authHeader && authHeader.startsWith(tokenHead)){
            // 字符串截取
            String authToken = authHeader.substring(tokenHead.length());
            // 根据token获取用户名
            String userName = jwtTokenUtil.getUserNameFromToken(authToken);
            // 能拿到用户名却拿不到全文对象---就是说token存在用户名但还未登录
            if(null!=userName && null== SecurityContextHolder.getContext().getAuthentication()){
                // 登录的话需要userdetailsService
                // 拿到 userDetails 后就等于登录了
                UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
                // 登录完成后去判断token是否有效，有效就重新放到用户对象中去，是********非常重要的一步，如果不设置，下一次在进行过滤器时
                // 依旧拿不到，就会继续走这一步，陷入死循环，所以获取到UserDetails之后，一定要把他设置回我们的全局上下文SpringSecurity中
                if(jwtTokenUtil.validateToken(authToken,userDetails)){
                    // UsernamePasswordAuthenticationToken继承AbstractAuthenticationToken实现Authentication
                    // 所以当在页面中输入用户名和密码之后首先会进入到UsernamePasswordAuthenticationToken验证(Authentication)，
                    //然后生成的Authentication会被交由AuthenticationManager来进行管理
                    //而AuthenticationManager管理一系列的AuthenticationProvider，
                    // 而每一个Provider都会通UserDetailsService和UserDetail来返回一个以UsernamePasswordAuthenticationToken实现的带用户名和密码以及权限的Authentication
                    // (用户名,密码,权限)
                    UsernamePasswordAuthenticationToken authenticationToken = new
                            UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    // 重新设置到用户对相中
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication((authenticationToken));
                }
            }
        }

        //关于chain.doFilter(request,response)
        //他的作用是将请求转发给过滤器链上下一个对象。这里的下一个指的是下一个filter，如果没有filter那就是你请求的资源。 一般filter都是一个链,web.xml 里面配置了几个就有几个。一个一个的连在一起
        //request -> filter1 -> filter2 ->filter3 -> .... -> request resource.
        filterChain.doFilter(request,response);
    }
}
